package visitor;

import syntaxtree.*;
import symbolTable.*;

public class PrimeiroVisitor implements Visitor {
	String classeAtual;
	String metodoAtual;
	SymbolTable tabela = new SymbolTable();

  // MainClass m;
  // ClassDeclList cl;
	
	
  public SymbolTable visit(Program n) {
	
    n.m.accept(this);
    for ( int i = 0; i < n.cl.size(); i++ ){
        n.cl.elementAt(i).accept(this);
    }
    return null;
  }
  
  // Identifier i1,i2;
  // Statement s;
  public void visit(MainClass n) {
    n.i1.accept(this);
    n.i2.accept(this);
    n.s.accept(this);
  }
  
  // Identifier i;
  // VarDeclList vl;
  // MethodDeclList ml;
  public void visit(ClassDeclSimple n) {
	
	if(tabela.findClass(n.i.s) != null){
		Erro.addError("A classe " + n.i.s + " ja foi declarada anteriormente");
	}
	tabela.addClass(n.i.s, null);
	classeAtual = n.i.s;
	metodoAtual = null;
	n.i.accept(this);
	for ( int i = 0; i < n.vl.size(); i++ ) {
	    n.vl.elementAt(i).accept(this);
	}
	for ( int i = 0; i < n.ml.size(); i++ ) {
	    n.ml.elementAt(i).accept(this);
	}
	classeAtual = null;
  }
 
  // Identifier i;
  // Identifier j;
  // VarDeclList vl;
  // MethodDeclList ml;
  public void visit(ClassDeclExtends n) {
	if(tabela.findClass(n.i.s) != null){
			Erro.addError("A classe " + n.i.s + " ja foi declarada anteriormente");
	}
	tabela.addClass(n.i.s, n.j.s);
	classeAtual = n.i.s;
	metodoAtual = null;
	
    n.i.accept(this);
    //n.j.accept(this);
    for ( int i = 0; i < n.vl.size(); i++ ) {
        n.vl.elementAt(i).accept(this);
    }
    for ( int i = 0; i < n.ml.size(); i++ ) {
        n.ml.elementAt(i).accept(this);
    }
    classeAtual = null;
  }

  // Type t;
  // Identifier i;
  public void visit(VarDecl n) {
	  if(metodoAtual == null){
		  if(tabela.findClassAttributeType(classeAtual, n.i.s) != null)
			  Erro.addError("O atributo " + n.i.s + " ja foi declarado anteriormente na classe " + classeAtual);
		  else
			  tabela.findClass(classeAtual).addVariable(n.i.s, n.t);
	  }
	  else{
		  if( tabela.findMethodLocalType(classeAtual, metodoAtual, n.i.s) != null){
			  Erro.addError("A variavel " + n.i.s + " ja foi declarada anteriormente no metodo " + metodoAtual+ " da classe " +classeAtual);
		  }
		  tabela.findClass(classeAtual).findMethod(metodoAtual).addLocal(n.i.s, n.t);
	  }
    n.t.accept(this);
    n.i.accept(this);
  }

  // Type t;
  // Identifier i;
  // FormalList fl;
  // VarDeclList vl;
  // StatementList sl;
  // Exp e;
  public void visit(MethodDecl n) {
	  
	if(tabela.findClass(classeAtual).findMethod(n.i.s)!= null){
		Erro.addError("O metodo " + n.i.s + " ja foi declarado anteriormente");
	}
	tabela.findClass(classeAtual).addMethod(n.i.s,n.t);
	metodoAtual = n.i.s;
	
    n.t.accept(this);
    n.i.accept(this);
    for ( int i = 0; i < n.fl.size(); i++ ) {
        n.fl.elementAt(i).accept(this);
    }
    for ( int i = 0; i < n.vl.size(); i++ ) {
        n.vl.elementAt(i).accept(this);
    }
    for ( int i = 0; i < n.sl.size(); i++ ) {
        n.sl.elementAt(i).accept(this);
    }
    n.e.accept(this);
    metodoAtual = null;
  }

  // Type t;
  // Identifier i;
  public void visit(Formal n) {
	  if(tabela.findMethodFormalType(classeAtual, metodoAtual, n.i.s) != null){
		  Erro.addError("O parametro " + n.i.s + " ja foi declarado anteriormente no metodo " + metodoAtual+ " da classe " +classeAtual);
	  }
	  if(tabela.findMethodLocalType(classeAtual, metodoAtual, n.i.s) != null){
		  Erro.addError("O parametro " + n.i.s + " ja foi declarado anteriormente como uma variavel do metodo " + metodoAtual+ " da classe " +classeAtual);
	  }
	  if(tabela.findClassAttributeType(classeAtual, n.i.s) != null){
		  Erro.addError("O parametro " + n.i.s + " ja foi declarado anteriormente como uma variavel na classe " +classeAtual);
	  }
	tabela.findClass(classeAtual).findMethod(metodoAtual).addParameter(n.i.s, n.t);
    n.t.accept(this);
    n.i.accept(this);
  }

  public void visit(IntArrayType n) {
  }

  public void visit(BooleanType n) {
  }

  public void visit(IntegerType n) {
  }

  // String s;
  public void visit(IdentifierType n) {
  }

  // StatementList sl;
  public void visit(Block n) {
    for ( int i = 0; i < n.sl.size(); i++ ) {
        n.sl.elementAt(i).accept(this);
    }
  }

  // Exp e;
  // Statement s1,s2;
  public void visit(If n) {
    n.e.accept(this);
    n.s1.accept(this);
    n.s2.accept(this);
  }

  // Exp e;
  // Statement s;
  public void visit(While n) {
    n.e.accept(this);
    n.s.accept(this);
  }

  // Exp e;
  public void visit(Print n) {
    n.e.accept(this);
  }
  
  // Identifier i;
  // Exp e;
  public void visit(Assign n) {
    n.i.accept(this);
    n.e.accept(this);
  }

  // Identifier i;
  // Exp e1,e2;
  public void visit(ArrayAssign n) {
    n.i.accept(this);
    n.e1.accept(this);
    n.e2.accept(this);
  }

  // Exp e1,e2;
  public void visit(And n) {
    n.e1.accept(this);
    n.e2.accept(this);
  }

  // Exp e1,e2;
  public void visit(LessThan n) {
    n.e1.accept(this);
    n.e2.accept(this);
  }

  // Exp e1,e2;
  public void visit(Plus n) {
    n.e1.accept(this);
    n.e2.accept(this);
  }

  // Exp e1,e2;
  public void visit(Minus n) {
    n.e1.accept(this);
    n.e2.accept(this);
  }

  // Exp e1,e2;
  public void visit(Times n) {
    n.e1.accept(this);
    n.e2.accept(this);
  }

  // Exp e1,e2;
  public void visit(ArrayLookup n) {
    n.e1.accept(this);
    n.e2.accept(this);
  }

  // Exp e;
  public void visit(ArrayLength n) {
    n.e.accept(this);
  }

  // Exp e;
  // Identifier i;
  // ExpList el;
  public void visit(Call n) {
    n.e.accept(this);
    n.i.accept(this);
    for ( int i = 0; i < n.el.size(); i++ ) {
        n.el.elementAt(i).accept(this);
    }
  }

  // int i;
  public void visit(IntegerLiteral n) {
  }

  public void visit(True n) {
  }

  public void visit(False n) {
  }

  // String s;
  public void visit(IdentifierExp n) {
  }

  public void visit(This n) {
  }

  // Exp e;
  public void visit(NewArray n) {
    n.e.accept(this);
  }

  // Identifier i;
  public void visit(NewObject n) {
  }

  // Exp e;
  public void visit(Not n) {
    n.e.accept(this);
  }

  // String s;
  public void visit(Identifier n) {
  }
}
